﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.IO;
using System.Windows.Forms;

using TLL.Debug;
using TLL.Finance;
using TLL.UTL;
using TLL.Finance.Currencys;
using TLL.UTL.Security;

namespace TLL.Repository
{
    abstract public class XMLRepository<T> : IDisposable where T:class
    {
        #region Varibles

            private bool access  = false;
            private Error error = null;
            private bool showError = false;
            private Warning warning = null;
            private bool showWarning = false;
            private string url = string.Empty;
            private string debugURL = string.Empty;
            private XmlDocument document = null;
            private XMLDirectionType directoryType = default(XMLDirectionType);
            private string codingKey = string.Empty;
            private NotifyIcon notify = null;

        #endregion

        #region Properties

            public bool Access { get { return this.access; } }
            public string DebugURL { get { return this.debugURL; } }
            public Error Error { get { return this.error; } protected set { this.error = value; } }
            public Warning Warning { get { return this.warning; } protected set { this.warning = value; } }
            public NotifyIcon Notify { get { return this.notify; } }
            public XmlDocument Document { get { return this.document; } }
            public string FileURL { get { return this.url; } }
            public bool isCoding { get { return this.codingKey != string.Empty; } }

        #endregion

        public XMLRepository(string url, string debugURL, string codingKey,  bool showError, bool showWarning, XMLDirectionType type = XMLDirectionType.Local, NotifyIcon notify = null)
        {
            this.showError = showError;
            this.showWarning = showWarning;
            this.debugURL = debugURL;
            this.url = url;
            this.directoryType = type;
            this.codingKey = codingKey;
            this.notify = notify;

            try
            {
                if (Coding.GetStatus(new string[] { url }))
                    Coding.CodingData(new string[] { url }, codingKey);

                using (var reader = XmlReader.Create(url))
                {
                    this.document = new XmlDocument();
                    this.document.Load(reader);
                }
                this.access = true;
                DebugLogRepository.Add(InformationType.Information, string.Format(@"Repository, Repository(string url[value = {0}]). Success operation", url), debugURL);
            }
            catch (Exception ex)
            {
                this.error = new Error(
                    ErrorType.ReadFile, 
                    new Dictionary<string, string>() { 
                        { "System error", ex.Message }, 
                        { "URL", url } 
                    });

                if (this.directoryType == XMLDirectionType.Web)
                    this.warning = new Warning(WarningType.Internet, this.notify, delegate(object sender, EventArgs e) {
                        this.error.Show(DisplayDebugType.MessageBox);
                    });

                DebugLogRepository.Add(InformationType.Error, string.Format(@"Class information: abstract public class Repository<T> : IDisposable where T:class
                                       Method information: public Repository(string url[value = {0}]) 
                                       Error title: {1}
                                       Error message: {2}", url, Error.Title, Error.Message), debugURL);
                this.access = false;
            }
        }
        public XMLRepository(string url, string debugURL, bool showError, bool showWarning, XMLDirectionType type = XMLDirectionType.Local, NotifyIcon notify = null) 
        {
            this.showError = showError;
            this.showWarning = showWarning;
            this.debugURL = debugURL;
            this.url = url;
            this.directoryType = type;
            this.notify = notify;

            try
            {
                using (var reader = XmlReader.Create(url))
                {
                    this.document = new XmlDocument();
                    this.document.Load(reader);
                }
                this.access = true;

                DebugLogRepository.Add(InformationType.Information, string.Format(@"Repository, Repository(string url[value = {0}]). Success operation", url), debugURL); 
            }
            catch (Exception ex)
            {
                this.error = new Error(
                    ErrorType.ReadFile,
                    new Dictionary<string, string>() { 
                        { "System error", ex.Message }, 
                        { "URL", url } 
                    });

                if (this.directoryType == XMLDirectionType.Web) 
                    this.warning = new Warning(WarningType.Internet, this.notify, delegate(object sender, EventArgs e) {
                        this.error.Show(DisplayDebugType.MessageBox);
                    });

                DebugLogRepository.Add(InformationType.Error, string.Format(@"Class information: abstract public class Repository<T> : IDisposable where T:class
                                       Method information: public Repository(string url[value = {0}]) 
                                       Error title: {1}
                                       Error message: {2}", url, Error.Title, Error.Message), debugURL);
                this.access = false; 
            }
        }
        public abstract List<T> Get(int id = 0);
        public virtual bool Create(T item) { throw new NotImplementedException(); }
        public virtual bool Update(T item, T newitem) { throw new NotImplementedException(); }
        public virtual bool Delete(int id) { throw new NotImplementedException(); }

        public void Dispose()
        {
            switch (this.directoryType)
            {
                case XMLDirectionType.Local:
                    if (this.access)
                    {
                        this.document.Save(this.url);
                        if (this.isCoding && !Coding.GetStatus(new string[] { this.url }))
                            Coding.CodingData(new string[] { this.url }, this.codingKey);
                    }
                    //else
                    //{
                    if (this.showError && this.error != null) this.error.Show(DisplayDebugType.MessageBox);
                    if (this.showWarning && this.warning != null) this.warning.Show(DisplayDebugType.Notify);
                    //}
                    break;
                case XMLDirectionType.Web:
                    if (this.showError && this.error != null && (!this.showWarning || (this.showWarning && this.warning == null)))
                        this.error.Show(DisplayDebugType.MessageBox);
                    if (this.showWarning && this.warning != null)
                        this.warning.Show(DisplayDebugType.Notify);
                    break;
            }
        }

        protected XmlNode Create_Node(string Type, List<XmlAttribute> Attributes)
        {
            XmlNode new_node = this.document.CreateElement(Type);
            foreach (XmlAttribute attribute in Attributes)
                new_node.Attributes.Append(attribute);
            return new_node;
        }
        protected XmlNode Create_Node(string Type, List<XmlAttribute> Attributes, XmlDocument document)
        {
            XmlNode new_node = document.CreateElement(Type);
            foreach (XmlAttribute attribute in Attributes)
                new_node.Attributes.Append(attribute);
            return new_node;
        }

        protected List<XmlAttribute> Create_Attributes(Dictionary<Enum, string> items)
        {
            var res = new List<XmlAttribute>();
            foreach (var item in items)
            {
                XmlAttribute attribute = this.document.CreateAttribute(item.Key.ToString());
                attribute.Value = item.Value;
                res.Add(attribute);
            }
            return res;
        }
        protected List<XmlAttribute> Create_Attributes(Dictionary<Enum, string> items, XmlDocument document)
        {
            var res = new List<XmlAttribute>();
            foreach (var item in items)
            {
                XmlAttribute attribute = document.CreateAttribute(item.Key.ToString());
                attribute.Value = item.Value;
                res.Add(attribute);
            }
            return res;
        }
        protected List<XmlAttribute> Create_Attributes(Dictionary<string, string> items)
        {
            var res = new List<XmlAttribute>();
            foreach (var item in items)
            {
                XmlAttribute attribute = document.CreateAttribute(item.Key);
                attribute.Value = item.Value;
                res.Add(attribute);
            }
            return res;
        }
        protected List<XmlAttribute> Create_Attributes(Dictionary<string, string> items, XmlDocument document)
        {
            var res = new List<XmlAttribute>();
            foreach (var item in items)
            {
                XmlAttribute attribute = document.CreateAttribute(item.Key);
                attribute.Value = item.Value;
                res.Add(attribute);
            }
            return res;
        }

        protected XmlAttribute Create_Attribute(Enum type, string value)
        {
            XmlAttribute attribute = this.document.CreateAttribute(type.ToString());
            attribute.Value = value;
            return attribute;
        }
        protected XmlAttribute Create_Attribute(string type, string value)
        {
            XmlAttribute attribute = this.document.CreateAttribute(type);
            attribute.Value = value;
            return attribute;
        }
        
        /// <summary>
        /// Check for the existence of the attribute
        /// </summary>
        /// <param name="item">node where check attribute</param>
        /// <param name="name">name of the attribute</param>
        /// <returns></returns>
        protected bool CheckAttribute(XmlNode item, string name)
        {
            var res = true;
            try { var st = item.Attributes[name].Value; }
            catch { res = false; }
            return res;
        }
        /// <summary>
        /// Check the attribute value
        /// </summary>
        /// <param name="item">node where check attribute</param>
        /// <param name="name">name of the attribute</param>
        /// <param name="value">attribute value</param>
        /// <returns></returns>
        protected bool CheckAttribute(XmlNode item, string name, string value, bool checkAttr)
        {
            var res = false;
            if (checkAttr)
            {
                if (this.CheckAttribute(item, name))
                    return item.Attributes[name].Value.Trim() == value.Trim();
            }
            else
                return item.Attributes[name].Value.Trim() == value.Trim();
            return res;
        }   

        protected T AttributeByType<T>(XmlNode node, Enum attributeName)
        {
            object res = null;
            try
            {
                string item = node.Attributes[attributeName.ToString()].Value;
                if (typeof(T) == typeof(int)) { res = int.Parse(item); return (T)res; }
                if (typeof(T) == typeof(bool)) { res = bool.Parse(item); return (T)res; }
                if (typeof(T) == typeof(DateTime)) { res = DateTime.Parse(item); return (T)res; }
                if (typeof(T) == typeof(DateTime?)) { res = DateTime.Parse(item); return (T)res; }
                if (typeof(T) == typeof(double)) { res = double.Parse(item); return (T)res; }
                if (typeof(T) == typeof(string)) { res = item.ToString(); return (T)res; }
                return default(T);
            }
            catch { return default(T); }
        }
        protected T AttributeByType<T>(XmlNode node, string attributeName)
        {
            object res = null;
            string item = node.Attributes[attributeName].Value;
            if (typeof(T) == typeof(int))
                res = int.Parse(item);
            if (typeof(T) == typeof(double))
                res = double.Parse(item);
            if (typeof(T) == typeof(string))
                res = item.ToString();
            if (typeof(T) == typeof(bool))
                res = bool.Parse(item);
            if (typeof(T) == typeof(DateTime))
                res = DateTime.Parse(item);
            if (typeof(T) == typeof(DateTime?))
                res = DateTime.Parse(item);

            return res != null ? (T)res : default(T);
        }

        protected XmlNode FindNode(XmlNode node, string name)
        {
            foreach (XmlNode childNode in node)
                if (childNode.Name == name)
                {
                    return childNode;
                }
                else if (childNode.ChildNodes.Count != 0)
                {
                    var findNode = FindNode(childNode, name);
                    if (findNode != null)
                        return findNode;
                }
            return null;
                    
        }
    }
}
